home *** CD-ROM | disk | FTP | other *** search
Text File | 1994-07-31 | 16.3 KB | 596 lines | [TEXT/MMCC] |
- /**************************************************************************
- LGBPushButton
-
- Public domain, by Zig Zichterman.
-
- This class implements 3D push buttons according to the guidelines
- suggested in _develop_ 15. Some of the drawing code is taken from
- the public domain source accompanying _develop_ 15.
-
- 07/31/94 zz Draw 3D if 4-bit grey, BW if 4-bit color
- 07/31/94 zz save/restore pen colors
- 1.0b3
- 07/28/94 zz use offscreen drawing for crisper feel
- 07/20/94 zz call PenNormal() before drawing anything!
- 1.0b1
- **************************************************************************/
- #include "LGBPushButton.h"
-
- #include <GestaltEqu.h>
-
- #include "LGBControl.h"
- #include "LGBDeviceIterator.h"
- #include "UGBDraw.h"
-
- const short LGBPushButton_radius = 10;
- const short LGBPushButton_innerRadius = 8;
-
- /**************************************************************************
- Main() [static]
-
- Main entry point for all push button calls. Dispatch according to
- message.
- **************************************************************************/
- long
- LGBPushButton::Main(short inVariation, ControlHandle ioControl,
- short inMsg, long ioParam)
- {
- long returnMe = 0;
-
- // lock the control handle for the duration of this call
- char state = ::HGetState((Handle) ioControl);
- ::HLock((Handle) ioControl);
-
- LGBPushButton button(*ioControl,(inVariation & useWFont)?true:false);
-
- switch (inMsg) {
- case drawCntl :
- button.Draw(ioParam);
- break;
-
- case testCntl :
- {
- Point hitPt;
- hitPt.h = LoWord(ioParam);
- hitPt.v = HiWord(ioParam);
- if (button.Test(hitPt)) {
- returnMe = inButton;
- }
- }
- break;
-
- case calcCRgns : // only called in 24-bit mode
- { // is 32-bit addressing off?
- long result = 0;
- OSErr err = ::Gestalt(gestaltAddressingModeAttr,&result);
- if (!err && ((result &
- (1L << gestalt32BitAddressing)) == 0)) {
- RgnHandle rgn = (RgnHandle)
- ::StripAddress((Ptr) ioParam);
- button.CalcCRgn(rgn);
- }
- }
- break;
-
- case calcCntlRgn : // only called in 32-bit mode
- {
- button.CalcCRgn((RgnHandle) ioParam);
- }
- break;
- }
-
- // unlock handle
- ::HSetState((Handle) ioControl,state);
- return returnMe;
- }
-
- //—————————————————————————————————————————————————————————————————————————
- // Constructor
- //—————————————————————————————————————————————————————————————————————————
-
- /**************************************************************************
- LGBControl(ControlHandle,Boolean)
-
- construct/initialize a control object. Just stores the control handle
- and the "should I use the window font?" flag in data members.
- **************************************************************************/
- LGBPushButton::LGBPushButton(ControlRecord *inControl, Boolean inUseWFont)
- : mControl(inControl), mUseWFont(inUseWFont)
- {
-
- }
-
- //—————————————————————————————————————————————————————————————————————————
- // Dispatch entry points
- //—————————————————————————————————————————————————————————————————————————
-
- /**************************************************************************
- Draw()
-
- Draw the control. inPartCode is a part code specifying which part of
- the control to draw, or 0 for the entire control.
-
- PushButtons only have inButton, so we ignore the inPartCode.
-
- Save the current drawing environment. Iterate through all the devices
- (screens), drawing the control in color or black and white depending
- on the screen depth. Once done, restore the drawing environment and
- return.
- **************************************************************************/
- void
- LGBPushButton::Draw(long inPartCode)
- {
- // if we're invisible, don't draw
- if (mControl->contrlVis == false) return;
-
- // save the font
- short font,size,mode,face;
- {
- GrafPtr port;
- ::GetPort(&port);
- font = port->txFont;
- size = port->txSize;
- mode = port->txMode;
- face = port->txFace;
- }
-
- // save the pen colors // will probably crash SEs, so test first
- RGBColor fore,back;
- if (UGBDraw::ColorQDIsPresent()) {
- ::GetForeColor(&fore);
- ::GetBackColor(&back);
- }
-
- // save the clip region
- RgnHandle saveClip = ::NewRgn();
- if (!saveClip) return;
- ::GetClip(saveClip);
-
- // make the pen something sensible
- ::PenNormal();
- ::ForeColor(blackColor);
- ::BackColor(whiteColor);
-
- // loop through all the devices (screens)
- UGBDraw::Offscreen offscreen;
- LGBDeviceIterator device;
- device.Init(mControl->contrlRect);
- short depth = 0;
- do {
- depth = device.Next();
- if (depth == 0) break; // all done with devices
- UGBDraw::OffscreenPre(offscreen);
- if ((depth < 4)
- || (depth == 4 && device.mDeviceIsColor)) {
- DrawBW();
- } else {
-
- // erase the the entire background in grey, so that we
- // get the 4 corners, but only if we're drawing offscreen.
- // Too flashy for onscreen drawing, but mandatory for
- // offscreen
- if (offscreen.gworld) {
- UGBDraw::PenNormal();
- ::EraseRect(&mControl->contrlRect);
- }
- DrawColor();
- }
- UGBDraw::OffscreenPost(offscreen);
- } while(true);
-
- // restore the clip region
- ::SetClip(saveClip);
- ::DisposeRgn(saveClip);
-
- // restore the pen
- if (UGBDraw::ColorQDIsPresent()) {
- ::RGBForeColor(&fore);
- ::RGBBackColor(&back);
- }
-
- // restore the font
- ::TextFont(font);
- ::TextSize(size);
- ::TextMode(mode);
- ::TextFace(face);
- }
-
- /**************************************************************************
- Test()
-
- Return inButton if the point is in our rect. If you play with the
- system button definition, it looks like the button tests true even
- for points outside the button's round rect, as long as the points
- are in the button's (non-round) rect.
- **************************************************************************/
- Boolean
- LGBPushButton::Test(Point inHitPt)
- {
- return ::PtInRect(inHitPt,&(mControl->contrlRect));
- }
-
- /**************************************************************************
- CalcCRgn()
-
- Calculate the control's region in the given region handle
- **************************************************************************/
- void
- LGBPushButton::CalcCRgn(RgnHandle ioRgn)
- {
- if (!ioRgn) return; // idiot resistance
- ::OpenRgn();
- ::FrameRoundRect(&(mControl->contrlRect),
- LGBPushButton_radius,LGBPushButton_radius);
- ::CloseRgn(ioRgn);
- }
-
- //—————————————————————————————————————————————————————————————————————————
- // Draw
- //—————————————————————————————————————————————————————————————————————————
-
- /**************************************************************************
- DrawBW()
-
- Draw the control in black and white. It is NOT safe to make any
- color QuickDraw calls here.
- **************************************************************************/
- void
- LGBPushButton::DrawBW(void)
- {
- // Set the pen to black, 1x1
- ::PenNormal();
-
- // erase the insides
- EraseInsides();
-
- // draw the frame
- DrawFrame();
-
- // draw the name
- const short hilite = mControl->contrlHilite;
- DrawName(hilite == 255);
-
- // draw highlight if necessary
- if (hilite && (hilite != 255)) {
- Rect invertMe = mControl->contrlRect;
- ::InsetRect(&invertMe,1,1);
- ::InvertRoundRect(&invertMe,
- LGBPushButton_innerRadius,LGBPushButton_innerRadius);
- }
- }
-
- /**************************************************************************
- DrawName()
-
- Draw the name. Any color stuff must already be set up by the caller.
- Pass in TRUE if for inDim1bit if you DrawName() to grey
- out the name for you (by applying a 50% dither).
- **************************************************************************/
- void
- LGBPushButton::DrawName(Boolean inDim1Bit)
- {
- // draw the name
- if (!mUseWFont) LGBControl::SetupFont();
-
- // calc the max bounds we'll be drawing into. We'll change
- // the vertical bounds later, but we need a starting point
- // for the clip setup below.
- Rect textRect = mControl->contrlRect;
- textRect.left++;
- textRect.top++;
- textRect.right -= 2;
- textRect.bottom--;
-
- // grab faster access to the title
- StringPtr title = mControl->contrlTitle;
-
- { // center vertically
- const short titleHeight = LGBControl::TitleHeight(title);
- short centerV = (textRect.top + textRect.bottom)/2;
- textRect.top = centerV - (titleHeight/2);
- textRect.bottom = textRect.top + titleHeight;
- }
-
- // clip to the insides of the button frame
- RgnHandle tempClip = ::NewRgn();
- RgnHandle currClip = ::NewRgn();
- if (tempClip && currClip) {
- // build up a region that we'd like to clip to
- ::OpenRgn();
- ::FrameRoundRect(&textRect,LGBPushButton_innerRadius,
- LGBPushButton_innerRadius);
- ::CloseRgn(tempClip);
- // get the current clip region
- ::GetClip(currClip);
- // find the intersection of the current and desired clip region,
- // set the clip region to that intersection
- ::SectRgn(tempClip,currClip,tempClip);
- ::SetClip(tempClip);
- ::DisposeRgn(tempClip);
-
- // draw the title. We use TextBox() to support
- // multiline titles: DrawString() no habla '\r'
- ::TextBox(title + 1,*title,&textRect,teCenter);
-
- // grey out the name if inactive
- if (inDim1Bit) {
- // fabricate a 50% grey dither through code.
- // this way we don't rely on QuickDraw globals
- unsigned char grey[8];
- grey[0] = grey[2] = grey[4] = grey[6] = 0xAA;
- grey[1] = grey[3] = grey[5] = grey[7] = 0x55;
- ::PenPat((PatPtr) grey);
- ::PenMode(patBic);
- ::PaintRect(&textRect);
- ::PenNormal();
- }
-
- // restore the clip region
- ::SetClip(currClip);
- ::DisposeRgn(currClip);
- }
- }
-
- /**************************************************************************
- DrawColor()
-
- Draw the control in color
- **************************************************************************/
- void
- LGBPushButton::DrawColor(void)
- {
- // active buttons draw 3D, inactive ones draw flat but grey
- if (mControl->contrlHilite == 255) {
- DrawColorInactive();
- } else {
- DrawColorActive();
- }
-
- // restore the pen
- ::PenNormal();
- UGBDraw::PenReallyNormal();
- }
-
- /**************************************************************************
- DrawColorInactive()
-
- Draw the control inactive, in color. Ignore the control's
- color table, just draw in grey
- **************************************************************************/
- void
- LGBPushButton::DrawColorInactive(void)
- {
- // draw frame
- UGBDraw::PenFrameInactive();
- DrawFrame();
-
- // erase background
- UGBDraw::Background();
- EraseInsides();
-
- // draw text. Rely on the side effects of erase backround
- // leaving the fore and back color
- DrawName();
-
- UGBDraw::PenNormal();
- }
-
- /**************************************************************************
- DrawColorActive()
-
- Draw the control in color, active. Ignore the color table
- except for any tinge colors
-
- ### zz 07/04/94 tinge colors not implemented yet, maybe never
- **************************************************************************/
- void
- LGBPushButton::DrawColorActive(void)
- {
- // erase the background
- if (mControl->contrlHilite) {
- UGBDraw::BackGrey(UGBDraw_hiliteBackground);
- } else {
- UGBDraw::Background();
- }
- EraseInsides();
-
- // draw the frame
- UGBDraw::PenFrameActive();
- DrawFrame();
-
- // draw the name
- DrawName();
-
- // draw the 3D effect
- Draw3DEffects();
- }
-
- /**************************************************************************
- DrawFrame()
-
- Draw the round rect for our button. No 3D effects, just a round rect.
- Caller should set up pen before this call
- **************************************************************************/
- void
- LGBPushButton::DrawFrame(void)
- {
- ::FrameRoundRect(&(mControl->contrlRect),
- LGBPushButton_radius,LGBPushButton_radius);
- }
-
- /**************************************************************************
- EraseInsides()
-
- Erase the insides of the button. Caller should set up the pen
- **************************************************************************/
- void
- LGBPushButton::EraseInsides(void)
- { // erase the insides
- Rect eraseMe = mControl->contrlRect;
- ::InsetRect(&eraseMe,1,1);
- ::EraseRoundRect(&eraseMe,
- LGBPushButton_innerRadius,LGBPushButton_innerRadius);
- }
-
- /**************************************************************************
- Draw3DEffects()
-
- Draw the spiffy 3D effects.
- **************************************************************************/
- void
- LGBPushButton::Draw3DEffects(void)
- {
- const Boolean hilite = (mControl->contrlHilite != 0);
-
- if (hilite) { // draw highlighted
- { // outer rim
- Rect outer = mControl->contrlRect;
- ::InsetRect(&outer,1,1);
-
- // outside edge, top left shadow
- UGBDraw::ForeGrey(UGBDraw_grey4);
- ::MoveTo(outer.left,outer.bottom - 3);
- ::LineTo(outer.left,outer.top + 2);
- ::MoveTo(outer.left + 2, outer.top);
- ::LineTo(outer.right - 3,outer.top);
-
- // outside edge, bottom right unshadow
- UGBDraw::ForeGrey(UGBDraw_greyC);
- ::MoveTo(outer.left + 2, outer.bottom - 1);
- ::LineTo(outer.right - 3, outer.bottom - 1);
- ::MoveTo(outer.right - 1, outer.bottom - 3);
- ::LineTo(outer.right - 1, outer.top + 2);
-
- // outside edge, top left corner
- UGBDraw::ForeGrey(UGBDraw_grey5);
- ::MoveTo(outer.left + 1, outer.top + 1);
- ::Line(0,0);
-
- // outside edge, top right corner
- UGBDraw::ForeGrey(UGBDraw_grey5);
- ::MoveTo(outer.right - 2, outer.top + 1);
- ::Line(0,0);
-
- // outside edge, bottom left corner
- UGBDraw::ForeGrey(UGBDraw_grey5);
- ::MoveTo(outer.left + 1, outer.bottom - 2);
- ::Line(0,0);
-
- // outside edge, bottom right corner
- UGBDraw::ForeGrey(UGBDraw_greyC);
- ::MoveTo(outer.right - 2, outer.bottom - 2);
- ::Line(0,0);
- }
-
- { // inner rim
- Rect inner = mControl->contrlRect;
- ::InsetRect(&inner,2,2);
-
- // inside edge, top left shadow
- UGBDraw::ForeGrey(UGBDraw_grey5);
- ::MoveTo(inner.left, inner.bottom - 2);
- ::LineTo(inner.left, inner.top + 1);
- ::MoveTo(inner.left + 1, inner.top);
- ::LineTo(inner.right - 2, inner.top);
-
- // inside edge, bottom right unshadow
- UGBDraw::ForeGrey(UGBDraw_greyA);
- ::MoveTo(inner.left + 1, inner.bottom - 1);
- ::LineTo(inner.right - 2, inner.bottom - 1);
- ::MoveTo(inner.right - 1, inner.bottom - 2);
- ::LineTo(inner.right - 1, inner.top + 1);
-
- // inside edge, top left corner
- UGBDraw::ForeGrey(UGBDraw_grey4);
- ::MoveTo(inner.left + 1, inner.top + 1);
- ::Line(0,0);
-
- // inside edge, top right corner
- UGBDraw::ForeGrey(UGBDraw_grey4);
- ::MoveTo(inner.right - 2, inner.top + 1);
- ::Line(0,0);
-
- // inside edge, bottom left corner
- UGBDraw::ForeGrey(UGBDraw_grey4);
- ::MoveTo(inner.left + 1, inner.bottom - 2);
- ::Line(0,0);
-
- // inside edge, bottom right corner
- UGBDraw::ForeGrey(UGBDraw_greyA);
- ::MoveTo(inner.right - 2, inner.bottom - 2);
- ::Line(0,0);
- }
- } else { // draw unhighlighted
- { // outer rim
- Rect outer = mControl->contrlRect;
- ::InsetRect(&outer,1,1);
-
- // outside edge, bottom left shadow
- UGBDraw::ForeGrey(UGBDraw_grey5);
- ::MoveTo(outer.left + 2,outer.bottom - 1);
- ::LineTo(outer.right - 3,outer.bottom - 1);
- ::MoveTo(outer.right - 1,outer.bottom - 3);
- ::LineTo(outer.right - 1,outer.top + 2);
-
- // outside edge, top left corner
- UGBDraw::ForeGrey(UGBDraw_greyF);
- ::MoveTo(outer.left + 1, outer.top + 1);
- ::Line(0,0);
-
- // outside edge, top right corner
- UGBDraw::ForeGrey(UGBDraw_greyB);
- ::MoveTo(outer.right - 2, outer.top + 1);
- ::Line(0,0);
-
- // outside edge, bottom left corner
- UGBDraw::ForeGrey(UGBDraw_greyB);
- ::MoveTo(outer.left + 1, outer.bottom - 2);
- ::Line(0,0);
-
- // outside edge, bottom right corner
- UGBDraw::ForeGrey(UGBDraw_grey5);
- ::MoveTo(outer.right - 2, outer.bottom - 2);
- ::Line(0,0);
- }
-
- { // inner rim
- Rect inner = mControl->contrlRect;
- ::InsetRect(&inner,2,2);
-
- // inside edge, top left unshadow
- UGBDraw::ForeGrey(UGBDraw_greyF);
- ::MoveTo(inner.left, inner.bottom - 2);
- ::LineTo(inner.left, inner.top + 1);
- ::MoveTo(inner.left + 1, inner.top);
- ::LineTo(inner.right - 2, inner.top);
-
- // inside edge, bottom right shadow
- UGBDraw::ForeGrey(UGBDraw_grey8);
- ::MoveTo(inner.left + 1, inner.bottom - 1);
- ::LineTo(inner.right - 2, inner.bottom - 1);
- ::MoveTo(inner.right - 1, inner.bottom - 2);
- ::LineTo(inner.right - 1, inner.top + 1);
-
- // inside edge, top left corner
- UGBDraw::ForeGrey(UGBDraw_greyF);
- ::MoveTo(inner.left + 1, inner.top + 1);
- ::Line(0,0);
-
- // inside edge, top right corner
- UGBDraw::ForeGrey(UGBDraw_greyB);
- ::MoveTo(inner.right - 2, inner.top + 1);
- ::Line(0,0);
-
- // inside edge, bottom left corner
- UGBDraw::ForeGrey(UGBDraw_greyB);
- ::MoveTo(inner.left + 1, inner.bottom - 2);
- ::Line(0,0);
-
- // inside edge, bottom right corner
- UGBDraw::ForeGrey(UGBDraw_grey8);
- ::MoveTo(inner.right - 2, inner.bottom - 2);
- ::Line(0,0);
- }
- }
- }
-